﻿#include "wgl.h"
FBconfig fbconfig;
WGL _wgl;
static bool StringInExtensionString(const char* string, const char* extensions)
{
    const char* start = extensions;

    for (;;)
    {
        const char* where;
        const char* terminator;

        where = strstr(start, string);
        if (!where)
            return false;

        terminator = where + strlen(string);
        if (where == start || *(where - 1) == ' ')
        {
            if (*terminator == ' ' || *terminator == '\0')
                break;
        }

        start = terminator;
    }

    return true;
}
static bool extensionSupportedWGL(const char* extension)
{
    const char* extensions;

    if (_wgl.GetExtensionsStringEXT)
    {
        extensions = _wgl.GetExtensionsStringEXT();
        if (extensions)
        {
            if (StringInExtensionString(extension, extensions))
                return true;
        }
    }

    if (_wgl.GetExtensionsStringARB)
    {
        extensions = _wgl.GetExtensionsStringARB(wglGetCurrentDC());
        if (extensions)
        {
            if (StringInExtensionString(extension, extensions))
                return true;
        }
    }

    return false;
}
static int getPixelFormatAttrib(HDC dc, int pixelFormat, int attrib)
{
    int value = 0;

    if (!_wgl.GetPixelFormatAttribivARB(dc,
                                   pixelFormat,
                                   0, 1, &attrib, &value))
    {
        qDebug()<<"Failed to retrieve pixel format attribute";
        return 0;
    }

    return value;
}
static const FBconfig* ChooseFBConfig(const FBconfig* desired,
                                         const FBconfig* alternatives,
                                         unsigned int count)
{
    unsigned int i;
    unsigned int missing, leastMissing = UINT_MAX;
    unsigned int colorDiff, leastColorDiff = UINT_MAX;
    unsigned int extraDiff, leastExtraDiff = UINT_MAX;
    const FBconfig* current;
    const FBconfig* closest = NULL;

    for (i = 0;  i < count;  i++)
    {
        current = alternatives + i;

        if (desired->stereo > 0 && current->stereo == 0)
        {
            // Stereo is a hard constraint
            continue;
        }

        if (desired->doublebuffer != current->doublebuffer)
        {
            // Double buffering is a hard constraint
            continue;
        }

        // Count number of missing buffers
        {
            missing = 0;

            if (desired->alphaBits > 0 && current->alphaBits == 0)
                missing++;

            if (desired->depthBits > 0 && current->depthBits == 0)
                missing++;

            if (desired->stencilBits > 0 && current->stencilBits == 0)
                missing++;

            if (desired->auxBuffers > 0 &&
                current->auxBuffers < desired->auxBuffers)
            {
                missing += desired->auxBuffers - current->auxBuffers;
            }

            if (desired->samples > 0 && current->samples == 0)
            {
                // Technically, several multisampling buffers could be
                // involved, but that's a lower level implementation detail and
                // not important to us here, so we count them as one
                missing++;
            }
        }

        // These polynomials make many small channel size differences matter
        // less than one large channel size difference

        // Calculate color channel size difference value
        {
            colorDiff = 0;

            if (desired->redBits != -1)
            {
                colorDiff += (desired->redBits - current->redBits) *
                             (desired->redBits - current->redBits);
            }

            if (desired->greenBits != -1)
            {
                colorDiff += (desired->greenBits - current->greenBits) *
                             (desired->greenBits - current->greenBits);
            }

            if (desired->blueBits != -1)
            {
                colorDiff += (desired->blueBits - current->blueBits) *
                             (desired->blueBits - current->blueBits);
            }
        }

        // Calculate non-color channel size difference value
        {
            extraDiff = 0;

            if (desired->alphaBits != -1)
            {
                extraDiff += (desired->alphaBits - current->alphaBits) *
                             (desired->alphaBits - current->alphaBits);
            }

            if (desired->depthBits != -1)
            {
                extraDiff += (desired->depthBits - current->depthBits) *
                             (desired->depthBits - current->depthBits);
            }

            if (desired->stencilBits != -1)
            {
                extraDiff += (desired->stencilBits - current->stencilBits) *
                             (desired->stencilBits - current->stencilBits);
            }

            if (desired->accumRedBits != -1)
            {
                extraDiff += (desired->accumRedBits - current->accumRedBits) *
                             (desired->accumRedBits - current->accumRedBits);
            }

            if (desired->accumGreenBits != -1)
            {
                extraDiff += (desired->accumGreenBits - current->accumGreenBits) *
                             (desired->accumGreenBits - current->accumGreenBits);
            }

            if (desired->accumBlueBits != -1)
            {
                extraDiff += (desired->accumBlueBits - current->accumBlueBits) *
                             (desired->accumBlueBits - current->accumBlueBits);
            }

            if (desired->accumAlphaBits != -1)
            {
                extraDiff += (desired->accumAlphaBits - current->accumAlphaBits) *
                             (desired->accumAlphaBits - current->accumAlphaBits);
            }

            if (desired->samples != -1)
            {
                extraDiff += (desired->samples - current->samples) *
                             (desired->samples - current->samples);
            }

            if (desired->sRGB && !current->sRGB)
                extraDiff++;
        }

        // Figure out if the current one is better than the best one found so far
        // Least number of missing buffers is the most important heuristic,
        // then color buffer size match and lastly size match for other buffers

        if (missing < leastMissing)
            closest = current;
        else if (missing == leastMissing)
        {
            if ((colorDiff < leastColorDiff) ||
                (colorDiff == leastColorDiff && extraDiff < leastExtraDiff))
            {
                closest = current;
            }
        }

        if (current == closest)
        {
            leastMissing = missing;
            leastColorDiff = colorDiff;
            leastExtraDiff = extraDiff;
        }
    }

    return closest;
}

static int choosePixelFormat(HDC dc, HDC helperDC)
{
    //loadextersions
    PIXELFORMATDESCRIPTOR pfd;
    HGLRC rc;
    HDC tdc = helperDC;

    ZeroMemory(&pfd, sizeof(pfd));
    pfd.nSize = sizeof(pfd);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 24;

    if (!SetPixelFormat(tdc, ChoosePixelFormat(tdc, &pfd), &pfd))
    {
        qDebug()<<"WGL: Failed to set pixel format for dummy context";
        return 0;
    }

    rc = wglCreateContext(tdc);
    if (!rc)
    {
        qDebug()<<"WGL: Failed to create dummy context";
        return 0;
    }

    if (!wglMakeCurrent(tdc, rc))
    {
        qDebug()<<"WGL: Failed to make dummy context current";
        wglDeleteContext(rc);
        return 0;
    }
    memset(&fbconfig,0,sizeof(fbconfig));
    fbconfig.redBits      = 8;
    fbconfig.greenBits    = 8;
    fbconfig.blueBits     = 8;
    fbconfig.alphaBits    = 8;
    fbconfig.depthBits    = 24;
    fbconfig.stencilBits  = 8;
    fbconfig.doublebuffer = 1;

    _wgl.WglGetProcAddress = (P_wglGetProcAddress)QLibrary::resolve("opengl32.dll","wglGetProcAddress");
    _wgl.GetPixelFormatAttribivARB = (PWGLGETPIXELFORMATATTRIBIVARBPROC)_wgl.WglGetProcAddress("wglGetPixelFormatAttribivARB");
    _wgl.CreateContextAttribsARB = (PWGLCREATECONTEXTATTRIBSARBPROC)_wgl.WglGetProcAddress("wglCreateContextAttribsARB");
    _wgl.GetExtensionsStringARB = (PWGLGETEXTENSIONSSTRINGARBPROC)_wgl.WglGetProcAddress("wglGetExtensionsStringARB");
    _wgl.GetExtensionsStringEXT = (PWGLGETEXTENSIONSSTRINGEXTPROC)_wgl.WglGetProcAddress("wglGetExtensionsStringEXT");
    _wgl.ARB_multisample =
        extensionSupportedWGL("WGL_ARB_multisample");
    _wgl.ARB_framebuffer_sRGB =
        extensionSupportedWGL("WGL_ARB_framebuffer_sRGB");
    _wgl.EXT_framebuffer_sRGB =
        extensionSupportedWGL("WGL_EXT_framebuffer_sRGB");
    _wgl.ARB_create_context =
        extensionSupportedWGL("WGL_ARB_create_context");
    _wgl.ARB_create_context_profile =
        extensionSupportedWGL("WGL_ARB_create_context_profile");
    _wgl.EXT_create_context_es2_profile =
        extensionSupportedWGL("WGL_EXT_create_context_es2_profile");
    _wgl.ARB_create_context_robustness =
        extensionSupportedWGL("WGL_ARB_create_context_robustness");
    _wgl.ARB_create_context_no_error =
        extensionSupportedWGL("WGL_ARB_create_context_no_error");
    _wgl.EXT_swap_control =
        extensionSupportedWGL("WGL_EXT_swap_control");
    _wgl.EXT_colorspace =
        extensionSupportedWGL("WGL_EXT_colorspace");
    _wgl.ARB_pixel_format =
        extensionSupportedWGL("WGL_ARB_pixel_format");
    _wgl.ARB_context_flush_control =
        extensionSupportedWGL("WGL_ARB_context_flush_control");
    wglMakeCurrent(tdc, NULL);
    wglDeleteContext(rc);

    //choosePixelFormat
    const FBconfig* closest;
    int i, pixelFormat, nativeCount, usableCount;
    usableCount = 0;
    nativeCount = getPixelFormatAttrib(dc,1,WGL_NUMBER_PIXEL_FORMATS_ARB);

    FBconfig* usableConfig = (FBconfig*)calloc(nativeCount,sizeof(FBconfig));
    for(int i = 0;i < nativeCount;++i)
    {
        const int n = i+1;
        FBconfig* u = usableConfig + usableCount;
        if(_wgl.ARB_pixel_format)
        {
            if (!getPixelFormatAttrib(dc, n, WGL_SUPPORT_OPENGL_ARB) ||
                !getPixelFormatAttrib(dc, n, WGL_DRAW_TO_WINDOW_ARB))
            {
                continue;
            }

            if (getPixelFormatAttrib(dc, n, WGL_PIXEL_TYPE_ARB) !=
                WGL_TYPE_RGBA_ARB)
            {
                continue;
            }

            if (getPixelFormatAttrib(dc, n, WGL_ACCELERATION_ARB) ==
                 WGL_NO_ACCELERATION_ARB)
            {
                continue;
            }

            u->redBits = getPixelFormatAttrib(dc, n, WGL_RED_BITS_ARB);
            u->greenBits = getPixelFormatAttrib(dc, n, WGL_GREEN_BITS_ARB);
            u->blueBits = getPixelFormatAttrib(dc, n, WGL_BLUE_BITS_ARB);
            u->alphaBits = getPixelFormatAttrib(dc, n, WGL_ALPHA_BITS_ARB);

            u->depthBits = getPixelFormatAttrib(dc, n, WGL_DEPTH_BITS_ARB);
            u->stencilBits = getPixelFormatAttrib(dc, n, WGL_STENCIL_BITS_ARB);

            u->accumRedBits = getPixelFormatAttrib(dc, n, WGL_ACCUM_RED_BITS_ARB);
            u->accumGreenBits = getPixelFormatAttrib(dc, n, WGL_ACCUM_GREEN_BITS_ARB);
            u->accumBlueBits = getPixelFormatAttrib(dc, n, WGL_ACCUM_BLUE_BITS_ARB);
            u->accumAlphaBits = getPixelFormatAttrib(dc, n, WGL_ACCUM_ALPHA_BITS_ARB);

            u->auxBuffers = getPixelFormatAttrib(dc, n, WGL_AUX_BUFFERS_ARB);

            if (getPixelFormatAttrib(dc, n, WGL_STEREO_ARB))
                u->stereo = true;
            if (getPixelFormatAttrib(dc, n, WGL_DOUBLE_BUFFER_ARB))
                u->doublebuffer = true;

            if (_wgl.ARB_multisample)
                u->samples = getPixelFormatAttrib(dc, n, WGL_SAMPLES_ARB);

            if (_wgl.ARB_framebuffer_sRGB ||
                _wgl.EXT_framebuffer_sRGB)
            {
                if (getPixelFormatAttrib(dc, n, WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB))
                    u->sRGB = true;
            }

            else
            {
                PIXELFORMATDESCRIPTOR pfd;

                // Get pixel format attributes through legacy PFDs

                if (!DescribePixelFormat(dc,
                                         n,
                                         sizeof(PIXELFORMATDESCRIPTOR),
                                         &pfd))
                {
                    continue;
                }

                if (!(pfd.dwFlags & PFD_DRAW_TO_WINDOW) ||
                    !(pfd.dwFlags & PFD_SUPPORT_OPENGL))
                {
                    continue;
                }

                if (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED) &&
                    (pfd.dwFlags & PFD_GENERIC_FORMAT))
                {
                    continue;
                }

                if (pfd.iPixelType != PFD_TYPE_RGBA)
                    continue;

                u->redBits = pfd.cRedBits;
                u->greenBits = pfd.cGreenBits;
                u->blueBits = pfd.cBlueBits;
                u->alphaBits = pfd.cAlphaBits;

                u->depthBits = pfd.cDepthBits;
                u->stencilBits = pfd.cStencilBits;

                u->accumRedBits = pfd.cAccumRedBits;
                u->accumGreenBits = pfd.cAccumGreenBits;
                u->accumBlueBits = pfd.cAccumBlueBits;
                u->accumAlphaBits = pfd.cAccumAlphaBits;

                u->auxBuffers = pfd.cAuxBuffers;

                if (pfd.dwFlags & PFD_STEREO)
                    u->stereo = true;
                if (pfd.dwFlags & PFD_DOUBLEBUFFER)
                    u->doublebuffer = true;
            }


            u->handle = n;
            usableCount++;
        }
    }
    if (!usableCount)
    {
        qDebug()<<"WGL: The driver does not appear to support OpenGL";

        free(usableConfig);
        return 0;
    }

    closest = ChooseFBConfig(&fbconfig, usableConfig, usableCount);
    if (!closest)
    {
        qDebug()<<"WGL: Failed to find a suitable pixel format";

        free(usableConfig);
        return 0;
    }
    pixelFormat = (int)closest->handle;
    free(usableConfig);

    return pixelFormat;
}
bool initWGL(HDC dc,HDC helperDC)
{
    int attribs[40];
    int pixelFormat;
    PIXELFORMATDESCRIPTOR pfd;
    HGLRC share = NULL;

    pixelFormat = choosePixelFormat(dc,helperDC);
    if(!pixelFormat)
        return false;

    if (!DescribePixelFormat(dc,
                             pixelFormat, sizeof(pfd), &pfd))
    {
        qDebug()<<"Failed to retrieve PFD for selected pixel format";
        return false;
    }
    if (!SetPixelFormat(dc, pixelFormat, &pfd))
    {
        qDebug()<<"WGL: Failed to set selected pixel format";
        return false;
    }

    attribs[0] = 0;
    attribs[1] = 0;

    HGLRC glrc = _wgl.CreateContextAttribsARB(dc,0,attribs);
    return wglMakeCurrent(dc,glrc);
}
